package com.eastfair.venueservice.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
//import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.eastfair.boot.request.PageParams;
import com.eastfair.boot.service.SuperServiceImpl;
import com.eastfair.constant.BusinessConstant;
import com.eastfair.core.base.R;
import com.eastfair.core.context.ContextUtil;
import com.eastfair.core.exception.BizException;
import com.eastfair.home.api.HomeWorkOvertimeFeign;
import com.eastfair.pay.dto.TransactionDTO;
import com.eastfair.pay.dto.TransactionOrderDTO;
import com.eastfair.pay.enumeration.TransactionOrderOrderStatusEnum;
import com.eastfair.pay.enumeration.TransactionOrderOrderTypeEnum;
import com.eastfair.pay.enums.InvoiceApplyBusinessTypeEnum;
import com.eastfair.pay.enums.TransactionStatusEnum;
import com.eastfair.pay.enums.TranstionTypeEnum;
import com.eastfair.projectcore.enumeration.MenuAffiliationEnum;
import com.eastfair.venueservice.consts.CommonConstants;
import com.eastfair.venueservice.dao.WorkOvertimeMapper;
import com.eastfair.venueservice.dto.ExhibitionSettlementDTO;
import com.eastfair.venueservice.dto.ServiceOrderDTO;
import com.eastfair.venueservice.dto.WorkOvertimeDTO;
import com.eastfair.venueservice.entity.CreatCode;
import com.eastfair.venueservice.entity.ServiceOrder;
import com.eastfair.venueservice.entity.WorkOvertime;
import com.eastfair.venueservice.enumeration.*;
import com.eastfair.venueservice.exceptioncode.VenueServiceExceptionCode;
import com.eastfair.venueservice.service.*;
import com.eastfair.venueservice.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 业务实现类
 * 加班申请
 * </p>
 *
 * @author dq
 * @date 2022-10-19
 */
@Slf4j
@Service
//@DS("#thread.tenant")
public class WorkOvertimeServiceImpl extends SuperServiceImpl<WorkOvertimeMapper, WorkOvertime> implements WorkOvertimeService {

    private static final String WORK_OVERTIME_NUMBER_FIELD = "work_overtime_number";

    private static final int WORK_OVERTIME_NUMBER_LENGTH = 5;

    @Value("${pay.payee.venue-manage}")
    private String venueManagePayee;

    @Resource
    private ServiceOrderService serviceOrderService;

    @Resource
    private WorkOvertimeRuleService workOvertimeRuleService;

    @Resource
    private ExhibitionServiceProviderService exhibitionServiceProviderService;

    @Resource
    private VenueManageTransactionService venueManageTransactionService;

    @Resource
    private VenueManageTransactionOrderService venueManageTransactionOrderService;

    @Resource
    private HomeWorkOvertimeFeign homeWorkOvertimeFeign;

    @Resource(name = "threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * 处理逻辑删除相关处理
     *
     * @param modelList 实体
     * @return 是否成功
     */
    @Override
    protected R<Boolean> handlerRemoveByIdsOfLogic(Collection<WorkOvertime> modelList) {
        modelList.forEach(s -> s.setIsDeleted(BusinessConstant.YES));
        return R.successDef();
    }

    @Override
    protected R<Boolean> handlerSave(WorkOvertime model) {
        model.setProjectId(ContextUtil.getProjectId());
        model.setSubsystemId(ContextUtil.getSubSystemId());
        if (StrUtil.isBlank(model.getAuditStatus())) {
            model.setAuditStatus(WorkOvertimeAuditStatusEnum.SUBMIT.getCode());
        }
        if (StrUtil.isBlank(model.getPayStatus())) {
            model.setPayStatus(WorkOvertimePayStatusEnum.NOT_PAY.getCode());
        }
        if (StrUtil.isBlank(model.getWorkOrderStatus())) {
            model.setWorkOrderStatus(WorkOvertimeWorkOrderStatusEnum.NOT_DISPATCH.getCode());
        }
        if (model.getWorkOvertimeType() == null) {
            model.setWorkOvertimeType(WorkOvertimeWorkOvertimeTypeEnum.WORK_OVERTIME_TYPE);
        }
        return R.successDef();
    }

    @Override
    public Page<WorkOvertimeVO> listWorkOvertimeVO(PageParams<WorkOvertimeDTO> pageParams, Function<WorkOvertimeDTO, QueryWrapper<WorkOvertime>> queryCreator) {
        log.info("listWorkOvertimeVO - 分页查询加班申请列表, pageParams={}", pageParams);
        Page<WorkOvertime> pageParam = pageParams.buildPage();
        QueryWrapper<WorkOvertime> queryWrapper = queryCreator.apply(pageParams.getModel());
        Page<WorkOvertime> page = page(pageParam, queryWrapper);
        Page<WorkOvertimeVO> voPage = new Page<>();
        BeanUtil.copyProperties(page, voPage);
        List<WorkOvertime> list = page.getRecords();
        if (list == null || list.isEmpty()) {
            return voPage;
        }
        List<WorkOvertimeVO> voList = list
                .stream()
                .map(workOvertime -> BeanUtil.toBean(workOvertime, WorkOvertimeVO.class))
                .collect(Collectors.toList());
        voPage.setRecords(voList);
        return voPage;
    }

    @Override
    public Page<WorkOvertimeVO> listForHomeBuilder(PageParams<WorkOvertimeDTO> pageParams) {
        WorkOvertimeDTO query = pageParams.getModel();
        List<String> belongSystemList = new ArrayList<>();
        belongSystemList.add(MenuAffiliationEnum.P_COMPOSITE_COURT.getCode());
        belongSystemList.add(MenuAffiliationEnum.P_COMPOSITE_PRODUCER.getCode());
        query.setBelongSystemList(belongSystemList);
        return listWorkOvertimeVO(pageParams, this::createQueryWrapperForHomeBuilder);
    }

    @Override
    public Page<WorkOvertimeVO> listForVenue(PageParams<WorkOvertimeDTO> pageParams) {
        WorkOvertimeDTO query = pageParams.getModel();
        query.setBelongSystem(MenuAffiliationEnum.P_COMPOSITE_VENUE.getCode());
        return listWorkOvertimeVO(pageParams, this::createQueryWrapperForVenue);
    }

    @Override
    public List<WorkOvertimeVO> listWorkOvertimeVO(WorkOvertimeDTO query) {
        log.info("listWorkOvertimeVO - 查询加班列表VO, query={}", query);
        List<WorkOvertime> list = listWorkOvertime(query);
        if (list == null || list.isEmpty()) {
            return Collections.emptyList();
        }
        List<WorkOvertimeVO> voList = list
                .stream()
                .map(workOvertime -> BeanUtil.toBean(workOvertime, WorkOvertimeVO.class))
                .collect(Collectors.toList());
        return voList;
    }

    @Override
    public List<WorkOvertime> listWorkOvertime(WorkOvertimeDTO workOvertimeDTO) {
        log.info("listWorkOvertime - 查询加班申请, workOvertimeDTO={}", workOvertimeDTO);
        QueryWrapper<WorkOvertime> queryWrapper = createWorkOvertimeQueryWrapper(workOvertimeDTO);
        return list(queryWrapper);
    }

    @Override
    public List<WorkOvertimeVO> listForVenueWorkOvertimeSettlement(WorkOvertimeDTO query) {
        log.info("listForVenueWorkOvertimeSettlement - 查询待结算的场馆加班单, query={}", query);
        QueryWrapper<WorkOvertime> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .lambda()
                .eq(WorkOvertime::getIsDeleted, BusinessConstant.DELETE_NO)
                .in(WorkOvertime::getBelongSystem, MenuAffiliationEnum.P_COMPOSITE_VENUE.getCode())
                .eq(query.getExhibitionManageId() != null, WorkOvertime::getExhibitionManageId, query.getExhibitionManageId())
                .eq(query.getHomeId() != null, WorkOvertime::getHomeId, query.getHomeId())
        ;
        List<WorkOvertime> list = list(queryWrapper);
        if (list == null || list.isEmpty()) {
            return Collections.emptyList();
        }
        return list
                .stream()
                .map(workOvertime -> BeanUtil.toBean(workOvertime, WorkOvertimeVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public ExhibitionSettlementVO getVenueWorkOvertimeSettlement(ExhibitionSettlementDTO query) {
        WorkOvertimeDTO overtimeQuery = new WorkOvertimeDTO();
        overtimeQuery.setExhibitionManageId(query.getExhibitionManageId());
        overtimeQuery.setHomeId(query.getCompanyId());
        List<WorkOvertimeVO> workOvertimeList = listForVenueWorkOvertimeSettlement(overtimeQuery);
        if (workOvertimeList == null || workOvertimeList.isEmpty()) {
            return null;
        }
        return WorkOvertimeService.calculateExhibitionSettlement(workOvertimeList);
    }

    @Override
    public List<WorkOvertimeVO> listForHomeWorkOvertimeSettlement(WorkOvertimeDTO query) {
        log.info("listForHomeWorkOvertimeSettlement - 查询待结算的主场加班单, query={}", query);
        QueryWrapper<WorkOvertime> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .lambda()
                .eq(WorkOvertime::getIsDeleted, BusinessConstant.DELETE_NO)
                .in(WorkOvertime::getBelongSystem, MenuAffiliationEnum.P_COMPOSITE_COURT.getCode(), MenuAffiliationEnum.P_COMPOSITE_PRODUCER.getCode())
                .eq(query.getExhibitionManageId() != null, WorkOvertime::getExhibitionManageId, query.getExhibitionManageId())
                .eq(query.getHomeId() != null, WorkOvertime::getHomeId, query.getHomeId())
                .eq(query.getCompanyId() != null, WorkOvertime::getCompanyId, query.getCompanyId())
                .eq(StrUtil.isNotBlank(query.getSpaceCode()), WorkOvertime::getSpaceCode, query.getSpaceCode())
                .eq(StrUtil.isNotBlank(query.getSpaceName()), WorkOvertime::getSpaceName, query.getSpaceName())
                .eq(StrUtil.isNotBlank(query.getSiteName()), WorkOvertime::getSiteName, query.getSiteName())
        ;
        List<WorkOvertime> list = list(queryWrapper);
        if (list == null || list.isEmpty()) {
            return Collections.emptyList();
        }
        return list
                .stream()
                .map(workOvertime -> BeanUtil.toBean(workOvertime, WorkOvertimeVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public ExhibitionSettlementVO getHomeWorkOvertimeSettlement(ExhibitionSettlementDTO query) {
        WorkOvertimeDTO overtimeQuery = new WorkOvertimeDTO();
        overtimeQuery.setExhibitionManageId(query.getExhibitionManageId());
        overtimeQuery.setCompanyId(query.getCompanyId());
        overtimeQuery.setSpaceCode(query.getSpaceCode());
        overtimeQuery.setSpaceName(query.getSpaceName());
        overtimeQuery.setSiteName(query.getSiteName());
        List<WorkOvertimeVO> workOvertimeList = listForHomeWorkOvertimeSettlement(overtimeQuery);
        if (workOvertimeList == null || workOvertimeList.isEmpty()) {
            return null;
        }
        return WorkOvertimeService.calculateExhibitionSettlement(workOvertimeList);
    }

    @Override
    public WorkOvertimeVO getVOByWorkOvertimeNumber(String workOvertimeNumber) {
        log.info("getVOByWorkOvertimeNumber - 加班单号获取加班详情VO, workOvertimeNumber={}", workOvertimeNumber);
        WorkOvertime workOvertime = getByWorkOvertimeNumber(workOvertimeNumber);
        if (workOvertime == null) {
            return null;
        }
        return BeanUtil.toBean(workOvertime, WorkOvertimeVO.class);
    }

    @Override
    public WorkOvertimeVO getVOByOrderNumber(String orderNumber) {
        log.info("getVOByOrderNumber - 订单号获取加班申请, orderNumber={}", orderNumber);
        WorkOvertime workOvertime = getByOrderNumber(orderNumber);
        if (workOvertime == null) {
            return null;
        }
        return BeanUtil.toBean(workOvertime, WorkOvertimeVO.class);
    }

    @Override
    public WorkOvertime getByWorkOvertimeNumber(String workOvertimeNumber) {
        QueryWrapper<WorkOvertime> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .lambda()
                .eq(WorkOvertime::getIsDeleted, BusinessConstant.DELETE_NO)
                .eq(StrUtil.isNotBlank(workOvertimeNumber),
                        WorkOvertime::getWorkOvertimeNumber, workOvertimeNumber);
        List<WorkOvertime> list = list(queryWrapper);
        if (list == null || list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    @Override
    public WorkOvertime getByOrderNumber(String orderNumber) {
        return getOne(new QueryWrapper<WorkOvertime>()
                .lambda()
                .eq(WorkOvertime::getIsDeleted, BusinessConstant.NO)
                .eq(WorkOvertime::getOrderNumber, orderNumber));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WorkOvertime saveWorkOvertime(WorkOvertimeDTO workOvertimeDTO) {
        log.info("saveWorkOvertime - 保存加班申请, workOvertimeDTO={}", workOvertimeDTO);
        // 生成加班单号
        String prefix = LocalDateTimeUtil.format(LocalDate.now(), "yyyyMMdd");
        String workOvertimeNumber = CreatCode.creatCode(baseMapper, WORK_OVERTIME_NUMBER_FIELD, WORK_OVERTIME_NUMBER_LENGTH, prefix);
        workOvertimeDTO.setWorkOvertimeNumber(workOvertimeNumber);
        // 保存加班申请
        WorkOvertime workOvertime = BeanUtil.toBean(workOvertimeDTO, WorkOvertime.class);
        save(workOvertime);
        return workOvertime;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public WorkOvertime updateWorkOvertime(WorkOvertimeDTO workOvertimeDTO) {
        log.info("updateWorkOvertime - 更新加班申请, workOvertimeDTO={}", workOvertimeDTO);
        WorkOvertime workOvertime = BeanUtil.toBean(workOvertimeDTO, WorkOvertime.class);
        updateById(workOvertime);
        return workOvertime;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WorkOvertime saveHomeBuilderSiteTypeWorkOvertime(WorkOvertimeDTO workOvertimeDTO) {
        // 计算加班面积
        BigDecimal overtimeArea = calculateOvertimeArea(workOvertimeDTO);
        workOvertimeDTO.setOvertimeArea(overtimeArea);
        // 查询场馆展位加班规则
        BigDecimal profitRate = BigDecimal.ZERO;
        WorkOvertimeRuleVO venueRule = workOvertimeRuleService.getVOByVenueOvertimeType(WorkOvertimeRuleOvertimeTypeEnum.SITE_OVERTIME, workOvertimeDTO.getSpecName());
        // 计算利润率
        if (venueRule != null) {
            profitRate = calculateHomeProfitRate(workOvertimeDTO, venueRule);
        }
        workOvertimeDTO.setProfitRate(profitRate);
        return saveWorkOvertime(workOvertimeDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WorkOvertime updateHomeBuilderSiteTypeWorkOvertime(WorkOvertimeDTO workOvertimeDTO) {
        // 计算加班面积
        BigDecimal overtimeArea = calculateOvertimeArea(workOvertimeDTO);
        workOvertimeDTO.setOvertimeArea(overtimeArea);
        // 查询场馆展位加班规则
        BigDecimal profitRate = BigDecimal.ZERO;
        WorkOvertimeRuleVO venueRule = workOvertimeRuleService.getVOByVenueOvertimeType(WorkOvertimeRuleOvertimeTypeEnum.SITE_OVERTIME, workOvertimeDTO.getSpecName());
        // 计算利润率
        if (venueRule != null) {
            profitRate = calculateHomeProfitRate(workOvertimeDTO, venueRule);
        }
        workOvertimeDTO.setProfitRate(profitRate);
        return updateWorkOvertime(workOvertimeDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean submitForBuilder(WorkOvertimeDTO workOvertimeDTO) {
        log.info("submitForBuilder - 搭建商申请加班, workOvertimeDTO={}", workOvertimeDTO);
        workOvertimeDTO.setBelongSystem(MenuAffiliationEnum.P_COMPOSITE_PRODUCER.getCode());
        workOvertimeDTO.setWorkOvertimeSource(WorkOvertimeWorkOvertimeSourceEnum.ONLINE_BOOK);
        // 查询负责搭建商的主场
        ServiceProviderVO home = exhibitionServiceProviderService
                .getHomeByExhibitionIdAndSpaceCode(workOvertimeDTO.getExhibitionManageId(), workOvertimeDTO.getSpaceCode());
        if (home == null) {
            throw BizException.wrap(-1, "负责的主场不存在，请联系主场");
        }
        workOvertimeDTO.setHomeId(home.getId());
        workOvertimeDTO.setHomeName(home.getCompanyName());
        WorkOvertime workOvertime;
        if (StrUtil.isBlank(workOvertimeDTO.getWorkOvertimeNumber())) {
            workOvertime = saveHomeBuilderSiteTypeWorkOvertime(workOvertimeDTO);
        } else {
            workOvertime = updateHomeBuilderSiteTypeWorkOvertime(workOvertimeDTO);
        }
        // 更新加班状态为已提交，等待主场审核
        updateWorkOvertimeSubmit(workOvertime.getWorkOvertimeNumber());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean submitForHome(WorkOvertimeDTO workOvertimeDTO) {
        log.info("submitForHome - 主场申请加班, workOvertimeDTO={}", workOvertimeDTO);
        workOvertimeDTO.setBelongSystem(MenuAffiliationEnum.P_COMPOSITE_COURT.getCode());
        workOvertimeDTO.setWorkOvertimeSource(WorkOvertimeWorkOvertimeSourceEnum.ONLINE_BOOK);
        // 主场申请的加班无需审核，直接审核通过
        workOvertimeDTO.setAuditStatus(WorkOvertimeAuditStatusEnum.APPROVED.getCode());
        workOvertimeDTO.setAuditTime(LocalDateTime.now());
        WorkOvertime workOvertime;
        if (StrUtil.isBlank(workOvertimeDTO.getWorkOvertimeNumber())) {
            workOvertime = saveHomeBuilderSiteTypeWorkOvertime(workOvertimeDTO);
        } else {
            workOvertime = updateHomeBuilderSiteTypeWorkOvertime(workOvertimeDTO);
        }
        // 主场加班申请向场馆报馆
        submitHomeWorkOvertimeTOVenue(workOvertime);
        // 主场提交的加班申请直接生成支付系统订单
        saveHomeTransactionOrder(workOvertime.getWorkOvertimeNumber());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean submitHomeWorkOvertimeTOVenue(WorkOvertime homeWorkOvertime) {
        log.info("submitForVenue - 向场馆提交加班申请, workOvertimeDTO={}", homeWorkOvertime);
        WorkOvertimeDTO venueWorkOvertimeDTO = homeWorkOvertimeToVenue(homeWorkOvertime);
        // 按照场馆加班规则计算加班金额
        WorkOvertimeRuleVO venueRule = workOvertimeRuleService.getVOByVenueOvertimeType(WorkOvertimeRuleOvertimeTypeEnum.SITE_OVERTIME, homeWorkOvertime.getSpecName());
        if (venueRule == null) {
            throw BizException.wrap(VenueServiceExceptionCode.VENUE_NOT_CONFIG_RULE.getCode(), "场馆未配置展位类型加班规则");
        }
        List<WorkOvertimeSpecVO> venueSpecList = venueRule.getWorkOvertimeSpecList();
        if (venueSpecList == null || venueSpecList.isEmpty()) {
            throw BizException.wrap(VenueServiceExceptionCode.VENUE_NOT_CONFIG_RULE.getCode(), "场馆未配置展位类型加班规格");
        }
        // 计算场馆加班的时段，时长，加班面积，加班金额
        calculateVenueWorkOvertime(venueWorkOvertimeDTO, venueRule);
        // 保存场馆加班申请
        WorkOvertime workOvertime = saveWorkOvertime(venueWorkOvertimeDTO);
        // 保存支付系统订单
        saveVenueTransactionOrder(workOvertime.getWorkOvertimeNumber());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancelHomeWorkOvertime(String workOvertimeNumber) {
        log.info("cancelHomeWorkOvertime - 主场取消加班申请, workOvertimeNumber={}", workOvertimeNumber);
        WorkOvertime workOvertime = getByWorkOvertimeNumber(workOvertimeNumber);
        if (workOvertime == null) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_NOT_EXIST);
        }
        // 更新加班申请状态为已取消
        updateWorkOvertimeCancel(workOvertime);
        // 取消支付系统订单
//        venueManageTransactionOrderService.cancel(workOvertimeNumber);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancelBuilderWorkOvertime(String workOvertimeNumber) {
        log.info("cancelBuilderWorkOvertime - 搭建商取消加班申请, workOvertimeNumber={}", workOvertimeNumber);
        WorkOvertime workOvertime = getByWorkOvertimeNumber(workOvertimeNumber);
        if (workOvertime == null) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_NOT_EXIST);
        }
        // 更新加班申请状态为已取消
        updateWorkOvertimeCancel(workOvertime);
        // 取消支付系统订单
//        venueManageTransactionOrderService.cancel(workOvertimeNumber);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean auditBuilderWorkOvertime(WorkOvertimeDTO workOvertimeDTO) {
        log.info("auditWorkOvertime - 加班申请审核, workOvertimeDTO={}", workOvertimeDTO);
        WorkOvertime workOvertime = updateWorkOvertimeAuditResult(workOvertimeDTO);
        if (WorkOvertimeAuditStatusEnum.APPROVED.eq(workOvertimeDTO.getAuditStatus())) {
            // 向场馆报馆
            submitHomeWorkOvertimeTOVenue(workOvertime);
            // 生成支付系统交易订单
            saveHomeTransactionOrder(workOvertimeDTO.getWorkOvertimeNumber());
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WorkOvertime updateWorkOvertimeSubmit(String workOvertimeNumber) {
        log.info("updateWorkOvertimeSubmit - 更新加班为待审核, workOvertime={}", workOvertimeNumber);
        WorkOvertime workOvertime = getByWorkOvertimeNumber(workOvertimeNumber);
        if (workOvertime == null) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_NOT_EXIST);
        }
        if (WorkOvertimeAuditStatusEnum.APPROVED.getCode().equals(workOvertime.getAuditStatus())) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_CAN_NOT_SUBMIT.getCode(), "加班已审核通过，无法重新申请加班");
        }
        if (WorkOvertimePayStatusEnum.HAS_PAY.getCode().equals(workOvertime.getPayStatus())) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_CAN_NOT_SUBMIT.getCode(), "加班已支付完成，无法重新申请加班");
        }
        workOvertime.setAuditStatus(WorkOvertimeAuditStatusEnum.SUBMIT.getCode());
        workOvertime.setAuditOpinion(null);
        workOvertime.setAuditTime(null);
        updateAllById(workOvertime);
        return workOvertime;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WorkOvertime updateWorkOvertimeCancel(WorkOvertime workOvertime) {
        log.info("updateWorkOvertimeCancel - 更新加班申请状态为已取消, workOvertime={}", workOvertime);
        if (WorkOvertimeAuditStatusEnum.CANCEL.getCode().equals(workOvertime.getAuditStatus())) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_CAN_NOT_CANCEL.getCode(), "加班申请已取消");
        }
        if (WorkOvertimePayStatusEnum.HAS_PAY.getCode().equals(workOvertime.getPayStatus())) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_CAN_NOT_CANCEL.getCode(), "加班申请已付款，无法取消");
        }
        workOvertime.setAuditStatus(WorkOvertimeAuditStatusEnum.CANCEL.getCode());
        workOvertime.setAuditOpinion(null);
        workOvertime.setAuditTime(null);
        updateAllById(workOvertime);
        return workOvertime;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WorkOvertime updateWorkOvertimeAuditResult(WorkOvertimeDTO workOvertimeDTO) {
        log.info("updateWorkOvertimeAuditResult - 更新审核结果, workOvertimeDTO={}", workOvertimeDTO);
        WorkOvertime workOvertime = getByWorkOvertimeNumber(workOvertimeDTO.getWorkOvertimeNumber());
        if (!WorkOvertimeAuditStatusEnum.SUBMIT.getCode().equals(workOvertime.getAuditStatus())) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_CAN_NOT_AUDIT);
        }
        // 审核加班申请
        workOvertime.setAuditStatus(workOvertimeDTO.getAuditStatus());
        workOvertime.setAuditOpinion(workOvertimeDTO.getAuditOpinion());
        workOvertime.setAuditTime(LocalDateTime.now());
        workOvertime.setAuditUserId(workOvertimeDTO.getAuditUserId());
        workOvertime.setAuditUserName(workOvertimeDTO.getAuditUserName());
        updateAllById(workOvertime);
        return workOvertime;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateWorkOvertimePayStatus(WorkOvertimeDTO workOvertimeDTO) {
        log.info("updateWorkOvertimePayStatus - 更新支付结果, workOvertimeDTO={}", workOvertimeDTO);
        WorkOvertime workOvertime = getByWorkOvertimeNumber(workOvertimeDTO.getWorkOvertimeNumber());
        if (workOvertime == null) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_NOT_EXIST);
        }
        // 线上预定的加班申请为审核通过不可支付
        if (!WorkOvertimeAuditStatusEnum.APPROVED.getCode().equals(workOvertime.getAuditStatus())
                && WorkOvertimeWorkOvertimeSourceEnum.ONLINE_BOOK.eq(workOvertime.getWorkOvertimeSource())) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_CAN_NOT_SUBMIT.getCode(), "加班申请未审核通过，无法支付");
        }
        if (WorkOvertimePayStatusEnum.HAS_PAY.getCode().equals(workOvertime.getPayStatus())) {
            throw BizException.wrap(-1, "加班申请已支付，无需再支付");
        }
        // 当前支付金额
        BigDecimal nowActualPayAmount = workOvertimeDTO.getActualPayAmount() == null ? BigDecimal.ZERO : workOvertimeDTO.getActualPayAmount();
        // 当前支付金额
        if (nowActualPayAmount == null || BigDecimal.ZERO.compareTo(nowActualPayAmount) == 0) {
            throw BizException.wrap(VenueServiceExceptionCode.PAY_AMOUNT_CAN_NOT_BE_ZERO);
        }
        // 加班申请目前实际已经支付金额
        BigDecimal actualPayAmount = workOvertime.getActualPayAmount() == null ? BigDecimal.ZERO : workOvertime.getActualPayAmount();
        // 加班申请应付金额
        BigDecimal shouldPayAmount = workOvertime.getShouldPayAmount() == null ? BigDecimal.ZERO : workOvertime.getShouldPayAmount();
        actualPayAmount = actualPayAmount.add(nowActualPayAmount);
        if (actualPayAmount.compareTo(shouldPayAmount) > 0) {
            throw BizException.wrap(VenueServiceExceptionCode.ACTUAL_PAY_AMOUNT_GT_SHOULD_PAY_AMOUNT);
        }
        workOvertime.setActualPayAmount(actualPayAmount);
        // 更新支付状态
        if (actualPayAmount.compareTo(shouldPayAmount) >= 0) {
            workOvertime.setPayStatus(WorkOvertimePayStatusEnum.HAS_PAY.getCode());
        }
        if (actualPayAmount.compareTo(BigDecimal.ZERO) > 0 && actualPayAmount.compareTo(shouldPayAmount) < 0) {
            workOvertime.setPayStatus(WorkOvertimePayStatusEnum.PARTIAL_PAY.getCode());
        }
        workOvertime.setPayWay(workOvertimeDTO.getPayWay());
        workOvertime.setPayTime(LocalDateTime.now());
        updateById(workOvertime);
        // 新增交易记录
        saveTransaction(workOvertime, workOvertimeDTO);
        return true;
    }

    @Override
    public boolean saveTransaction(WorkOvertime workOvertime, WorkOvertimeDTO workOvertimeDTO) {
        log.info("saveTransaction - 保存交易记录, workOvertime={}, workOvertimeDTO={}", workOvertime, workOvertimeDTO);
        TransactionDTO transactionDTO = new TransactionDTO();
        transactionDTO.setExhibitionId(workOvertime.getExhibitionManageId());
        transactionDTO.setExhibitionName(workOvertime.getExhibitionManageName());
        transactionDTO.setSpaceName(workOvertime.getSpaceName());
        transactionDTO.setSiteName(workOvertime.getSiteName());
        transactionDTO.setServiceProviderId(workOvertime.getCompanyId());
        transactionDTO.setServiceProviderName(workOvertime.getCompanyName());
        transactionDTO.setServiceProviderType(workOvertime.getCompanyType());
        transactionDTO.setOrderType(InvoiceApplyBusinessTypeEnum.WORK_OVERTIME.getCode());
        transactionDTO.setOrderNum(workOvertime.getWorkOvertimeNumber());
        transactionDTO.setOrderAmount(workOvertimeDTO.getActualPayAmount());
        transactionDTO.setOrderTime(LocalDateTime.now());
        transactionDTO.setTransactionType(TranstionTypeEnum.PAYED.getCode());
        transactionDTO.setTransactionState(TransactionStatusEnum.PAYED.getCode());
        transactionDTO.setPayer(workOvertimeDTO.getPayer());
        transactionDTO.setPayType(workOvertimeDTO.getPayWay());
        transactionDTO.setSystemScene(ContextUtil.getSystemScene());
        return venueManageTransactionService.save(transactionDTO);
    }

    @Override
    public boolean saveTransactionOrder(WorkOvertimeDTO workOvertimeDTO) {
        log.info("saveTransactionOrder - 保存支付系统交易订单, workOvertimeDTO={}", workOvertimeDTO);
        TransactionOrderDTO transactionOrderDTO = new TransactionOrderDTO();
        transactionOrderDTO.setExhibitionId(workOvertimeDTO.getExhibitionManageId());
        transactionOrderDTO.setExhibitionName(workOvertimeDTO.getExhibitionManageName());
        transactionOrderDTO.setOrderType(TransactionOrderOrderTypeEnum.WORKEXTRA);
        transactionOrderDTO.setOrderNum(workOvertimeDTO.getWorkOvertimeNumber());
        transactionOrderDTO.setOrderAmount(workOvertimeDTO.getShouldPayAmount());
        transactionOrderDTO.setOrderTime(workOvertimeDTO.getReportTime());
        transactionOrderDTO.setOrderStatus(TransactionOrderOrderStatusEnum.UNPAY);
        transactionOrderDTO.setPayer(workOvertimeDTO.getPayer());
        transactionOrderDTO.setPayee(workOvertimeDTO.getPayee());
        transactionOrderDTO.setPaymentPeriods(1);
        transactionOrderDTO.setSystemCode(workOvertimeDTO.getSystemCode());
        return venueManageTransactionOrderService.save(transactionOrderDTO);
    }

    @Override
    public boolean saveVenueTransactionOrder(String workOvertimeNumber) {
        WorkOvertime workOvertime = getByWorkOvertimeNumber(workOvertimeNumber);
        if (workOvertime == null) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_NOT_EXIST.getCode(), "加班申请不存在");
        }
        WorkOvertimeDTO workOvertimeDTO = BeanUtil.toBean(workOvertime, WorkOvertimeDTO.class);
        workOvertimeDTO.setSystemCode(MenuAffiliationEnum.P_COMPOSITE_VENUE.getCode());
        workOvertimeDTO.setPayee(venueManagePayee);
        workOvertimeDTO.setPayer(workOvertime.getHomeName());
        return saveTransactionOrder(workOvertimeDTO);
    }

    @Override
    public boolean saveHomeTransactionOrder(String workOvertimeNumber) {
        WorkOvertime workOvertime = getByWorkOvertimeNumber(workOvertimeNumber);
        if (workOvertime == null) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_NOT_EXIST.getCode(), "加班申请不存在");
        }
        WorkOvertimeDTO workOvertimeDTO = BeanUtil.toBean(workOvertime, WorkOvertimeDTO.class);
        workOvertimeDTO.setSystemCode(MenuAffiliationEnum.P_COMPOSITE_COURT.getCode());
        workOvertimeDTO.setPayee(workOvertime.getHomeName());
        workOvertimeDTO.setPayer(workOvertime.getCompanyName());
        try {
            return saveTransactionOrder(workOvertimeDTO);
        } catch (BizException e) {
            // 发送配置收款账号的站内信
            if (e.getCode() == VenueServiceExceptionCode.PAYEE_NOT_CONFIG.getCode()) {
                asyncSendConfigHomePayeeInnerMsg(workOvertimeDTO);
                return true;
            }
            throw e;
        }
    }

    @Override
    public boolean sendConfigHomePayeeInnerMsg(WorkOvertimeDTO workOvertimeDTO) {
        log.info("sendConfigPayeeInnerMsg - 发送配置收款商户的站内信, workOvertimeDTO={}", workOvertimeDTO);
        R<Boolean> result = homeWorkOvertimeFeign.sendConfigPayeeInnerMsg(workOvertimeDTO);
        log.info("sendConfigPayeeInnerMsg - 发送配置收款商户的站内信, result={}", result);
        if (!result.getIsSuccess()) {
            throw BizException.wrap(result.getCode(), result.getMsg());
        }
        return result.getData();
    }

    @Override
    public void asyncSendConfigHomePayeeInnerMsg(WorkOvertimeDTO workOvertimeDTO) {
        threadPoolTaskExecutor.execute(() -> sendConfigHomePayeeInnerMsg(workOvertimeDTO));
    }

    /**
     * 保存加班服务订单
     *
     * @param workOvertime WorkOvertime
     * @return boolean.
     */
    @Deprecated
    private ServiceOrder saveServiceOrder(WorkOvertime workOvertime) {
        ServiceOrderDTO serviceOrderDTO = toServiceOrderDTO(workOvertime);
        return serviceOrderService.saveServiceOrder(serviceOrderDTO);
    }

    /**
     * WorkOvertime 转为 ServiceOrderDTO.
     *
     * @param workOvertime WorkOvertime.
     * @return ServiceOrderDTO.
     */
    private ServiceOrderDTO toServiceOrderDTO(WorkOvertime workOvertime) {
        ServiceOrderDTO serviceOrderDTO = new ServiceOrderDTO();
        serviceOrderDTO.setOrderSource(ServiceOrderOrderSourceEnum.WORK_OVERTIME_ORDER);
        serviceOrderDTO.setTotalAmount(workOvertime.getOvertimeAmount());
        serviceOrderDTO.setSurcharge(BigDecimal.ZERO);
        serviceOrderDTO.setExhibitionManageId(workOvertime.getExhibitionManageId());
        serviceOrderDTO.setExhibitionManageName(workOvertime.getExhibitionManageName());
        serviceOrderDTO.setCompanyId(workOvertime.getCompanyId());
        serviceOrderDTO.setCompanyCode(workOvertime.getCompanyCode());
        serviceOrderDTO.setCompanyName(workOvertime.getCompanyName());
        serviceOrderDTO.setCompanyType(workOvertime.getCompanyType());
        serviceOrderDTO.setContact(workOvertime.getContact());
        serviceOrderDTO.setPhone(workOvertime.getPhone());
        serviceOrderDTO.setSpaceCode(workOvertime.getSpaceCode());
        serviceOrderDTO.setSpaceName(workOvertime.getSpaceName());
        serviceOrderDTO.setSiteName(workOvertime.getSiteName());
        serviceOrderDTO.setShouldPayAmount(workOvertime.getShouldPayAmount());
        serviceOrderDTO.setActualPayAmount(workOvertime.getActualPayAmount());
        serviceOrderDTO.setDiscountAmount(BigDecimal.ZERO);
        serviceOrderDTO.setPayWay(workOvertime.getPayWay());
        serviceOrderDTO.setPayTime(workOvertime.getPayTime());
        serviceOrderDTO.setPayStatus(ServiceOrderPayStatusEnum.get(workOvertime.getPayStatus()));
        serviceOrderDTO.setWorkOrderStatus(ServiceOrderWorkOrderStatusEnum.get(workOvertime.getWorkOrderStatus()));
        serviceOrderDTO.setOrderSource(ServiceOrderOrderSourceEnum.WORK_OVERTIME_ORDER);
        serviceOrderDTO.setOrderStatus(ServiceOrderOrderStatusEnum.SUBMIT);
        serviceOrderDTO.setOrderTime(LocalDateTime.now());
        return serviceOrderDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancelVenueWorkOvertime(String workOvertimeNumber) {
        log.info("cancelVenueWorkOvertime - 取消场馆的加班申请, workOvertimeNumber={}", workOvertimeNumber);
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeByWorkOvertimeNumber(String workOvertimeNumber) {
        log.info("removeByWorkOvertimeNumber - 加班单号删除, workOvertimeNumber={}", workOvertimeNumber);
        WorkOvertime workOvertime = getByWorkOvertimeNumber(workOvertimeNumber);
        if (workOvertime == null) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_NOT_EXIST.getCode(), "无法删除，加班申请不存在");
        }
        if (WorkOvertimeAuditStatusEnum.CANCEL.getCode().equals(workOvertime.getAuditStatus())) {
            throw BizException.wrap(VenueServiceExceptionCode.WORK_OVERTIME_CAN_NOT_DELETE.getCode(), "无法删除，加班申请必须先取消");
        }
        List<WorkOvertime> list = new ArrayList<>();
        list.add(workOvertime);
        return removeByIdsOfLogic(list);
    }

    /**
     * 创建查询WorkOvertime的QueryWrapper
     *
     * @param param WorkOvertimeDTO.
     * @return QueryWrapper.
     */
    private QueryWrapper<WorkOvertime> createWorkOvertimeQueryWrapper(WorkOvertimeDTO param) {
        QueryWrapper<WorkOvertime> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .lambda()
                .eq(WorkOvertime::getIsDeleted, BusinessConstant.DELETE_NO)
                .eq(param.getExhibitionManageId() != null, WorkOvertime::getExhibitionManageId, param.getExhibitionManageId())
                .eq(param.getCompanyId() != null, WorkOvertime::getCompanyId, param.getCompanyId())
                .eq(StrUtil.isNotBlank(param.getCompanyCode()), WorkOvertime::getCompanyCode, param.getCompanyCode())
                .eq(StrUtil.isNotBlank(param.getSpaceCode()), WorkOvertime::getSpaceCode, param.getSpaceCode())
                .eq(StrUtil.isNotBlank(param.getSiteName()), WorkOvertime::getSiteName, param.getSiteName())
                .eq(param.getWorkOvertimeType() != null, WorkOvertime::getWorkOvertimeType, param.getWorkOvertimeType())
                .eq(param.getWorkOvertimeSource() != null, WorkOvertime::getWorkOvertimeSource, param.getWorkOvertimeSource())
                .in(param.getIdList() != null && !param.getIdList().isEmpty(), WorkOvertime::getId, param.getIdList())
                .in(param.getWorkOvertimeNumberList() != null && !param.getWorkOvertimeNumberList().isEmpty(),
                        WorkOvertime::getWorkOvertimeNumber, param.getWorkOvertimeNumberList())
                .in(param.getBelongSystemList() != null && !param.getBelongSystemList().isEmpty(), WorkOvertime::getBelongSystem, param.getBelongSystemList())
                .eq(StrUtil.isNotBlank(param.getBelongSystem()), WorkOvertime::getBelongSystem, param.getBelongSystem())
                .and(StrUtil.isNotBlank(param.getKeyword()), keywordQueryWrapper -> keywordQueryWrapper
                        .like(WorkOvertime::getWorkOvertimeNumber, param.getKeyword())
                        .or()
                        .like(WorkOvertime::getCompanyName, param.getKeyword())
                        .or()
                        .like(WorkOvertime::getSpaceName, param.getKeyword())
                        .or()
                        .like(WorkOvertime::getSiteName, param.getKeyword()))
        ;
        return queryWrapper;
    }

    /**
     * 主场搭建商查询的query wrapper
     *
     * @param param the param
     * @return the query wrapper
     */
    public QueryWrapper<WorkOvertime> createQueryWrapperForHomeBuilder(WorkOvertimeDTO param) {
        QueryWrapper<WorkOvertime> queryWrapper = createWorkOvertimeQueryWrapper(param);
        queryWrapper
                .lambda()
                .and(param.getHomeId() != null, homeQueryWrapper -> homeQueryWrapper
                        .eq(param.getHomeId() != null, WorkOvertime::getHomeId, param.getHomeId())
                        .ne(WorkOvertime::getAuditStatus, WorkOvertimeAuditStatusEnum.CANCEL))
                .eq(StrUtil.isNotBlank(param.getCompanyType()), WorkOvertime::getCompanyType, param.getCompanyType())
                .eq(StrUtil.isNotBlank(param.getSpaceCode()), WorkOvertime::getSpaceCode, param.getSpaceCode())
                .like(StrUtil.isNotBlank(param.getSpaceName()), WorkOvertime::getSpaceName, param.getSpaceName())
                .like(StrUtil.isNotBlank(param.getSiteName()), WorkOvertime::getSiteName, param.getSiteName())
                .eq(StrUtil.isNotBlank(param.getAuditStatus()), WorkOvertime::getAuditStatus, param.getAuditStatus())
                .eq(param.getOvertimeDate() != null, WorkOvertime::getOvertimeDate, param.getOvertimeDate())
                .eq(StrUtil.isNotBlank(param.getPayStatus()), WorkOvertime::getPayStatus, param.getPayStatus())
        ;
        return queryWrapper;
    }

    /**
     * 场馆查询的query wrapper
     *
     * @param param the param
     * @return the query wrapper
     */
    public QueryWrapper<WorkOvertime> createQueryWrapperForVenue(WorkOvertimeDTO param) {
        QueryWrapper<WorkOvertime> queryWrapper = createWorkOvertimeQueryWrapper(param);
        queryWrapper
                .lambda()
                .like(StrUtil.isNotBlank(param.getWorkOvertimeNumber()),
                        WorkOvertime::getWorkOvertimeNumber, param.getWorkOvertimeNumber())
                .eq(StrUtil.isNotBlank(param.getRuleType()), WorkOvertime::getRuleType, param.getRuleType())
                .eq(param.getWorkOrderStatus() != null, WorkOvertime::getWorkOrderStatus, param.getWorkOrderStatus())
        ;
        return queryWrapper;
    }

    /**
     * 展位类型加班计算加班面积.
     *
     * @param workOvertimeDTO the work overtime dto
     * @return the big decimal
     */
    private static BigDecimal calculateOvertimeArea(WorkOvertimeDTO workOvertimeDTO) {
        BigDecimal siteArea = workOvertimeDTO.getSiteArea() == null ? BigDecimal.ZERO : workOvertimeDTO.getSiteArea();
        BigDecimal minArea = workOvertimeDTO.getMinArea() == null ? BigDecimal.ZERO : workOvertimeDTO.getMinArea();
        return WorkOvertimeService.calculateOvertimeArea(siteArea, minArea);
    }

    /**
     * 主场计算相对场馆的收费的利润率.
     *
     * @param homeWorkOvertimeDTO the work overtime dto
     */
    private static BigDecimal calculateHomeProfitRate(WorkOvertimeDTO homeWorkOvertimeDTO, WorkOvertimeRuleVO venueRule) {
        List<WorkOvertimeSpecVO> venueSpecList = venueRule.getWorkOvertimeSpecList();
        if (venueSpecList == null || venueSpecList.isEmpty()) {
            return BigDecimal.ZERO;
        }
        // 通过加班申请选择的规格确定规格
        WorkOvertimeSpecVO venueSpec = venueSpecList.get(0);
        if (venueSpec == null) {
            return BigDecimal.ZERO;
        }
        WorkOvertime homeWorkOvertime = BeanUtil.toBean(homeWorkOvertimeDTO, WorkOvertime.class);
        WorkOvertimeDTO venueWorkOvertimeDTO = homeWorkOvertimeToVenue(homeWorkOvertime);
        BeanUtil.copyProperties(homeWorkOvertimeDTO, venueWorkOvertimeDTO);
        // 计算场馆加班的金额等数据
        calculateVenueWorkOvertime(venueWorkOvertimeDTO,venueRule);
        // 应付场馆加班费
        BigDecimal shouldPayVenueAmount = venueWorkOvertimeDTO.getShouldPayAmount();
        // 应付主场加班费
        BigDecimal shouldPayHomeAmount = homeWorkOvertimeDTO.getShouldPayAmount();
        // 主场利润
        BigDecimal homeProfit = shouldPayHomeAmount.subtract(shouldPayVenueAmount);
        // 主场利润率
        return homeProfit
                .divide(shouldPayVenueAmount, 4, RoundingMode.DOWN)
                .multiply(BigDecimal.valueOf(100L));
    }

    /**
     * 按照场馆的加班规则计算场馆加班的加班时长，加班面积，加班金额
     *
     * @param venueWorkOvertimeDTO the venue work overtime dto
     * @param venueRule            the venue rule
     */
    private static void calculateVenueWorkOvertime(WorkOvertimeDTO venueWorkOvertimeDTO, WorkOvertimeRuleVO venueRule) {
        // 场馆加班规则计算加班时段，时长
        calculateVenuePeriod(venueWorkOvertimeDTO, venueRule);
        // 计算加班面积
        venueWorkOvertimeDTO.setMinArea(venueRule.getMinArea());
        BigDecimal overtimeArea = calculateOvertimeArea(venueWorkOvertimeDTO);
        venueWorkOvertimeDTO.setOvertimeArea(overtimeArea);
        // 场馆加班规则计算加班金额
        calculateVenueOvertimeAmount(venueWorkOvertimeDTO, venueRule);
    }

    /**
     * 计算场馆加班申请的加班金额，超时加急金额，应付总金额.
     *
     * @param workOvertimeDTO the work overtime dto
     * @param venueRule       the venue rule
     */
    private static void calculateVenueOvertimeAmount(WorkOvertimeDTO workOvertimeDTO, WorkOvertimeRuleVO venueRule) {
        List<WorkOvertimeSpecVO> venueSpecList = venueRule.getWorkOvertimeSpecList();
        // 场馆时段单价
        WorkOvertimeSpecVO venueSpec = venueSpecList.get(0);
        BigDecimal venuePeriodOnePrice = venueSpec.getPeriodOnePrice();
        BigDecimal venuePeriodTwoPrice = venueSpec.getPeriodTwoPrice();
        Integer timeUnit = venueRule.getTimeUnit();
        // 应付场馆加班费
        BigDecimal overtimeAmount = WorkOvertimeService.calculateOvertimeAmount(venuePeriodOnePrice, workOvertimeDTO.getPeriodOne(), venuePeriodTwoPrice, workOvertimeDTO.getPeriodTwo(), timeUnit, workOvertimeDTO.getOvertimeArea());
        BigDecimal shouldPayVenueAmount = overtimeAmount;
        // 场馆申报截止时间
        LocalTime venueSubmitDeadline = venueRule.getSubmitDeadline();
        LocalDateTime venueSubmitDeadlineDateTime = LocalDateTime.of(workOvertimeDTO.getOvertimeDate(), venueSubmitDeadline);
        // 加班申请时间
        LocalDateTime reportDateTime = workOvertimeDTO.getReportTime();
        // 判断是否需要加收，如果加班申请时间早于场馆，则不需要收申报加收费
        if (reportDateTime.isAfter(venueSubmitDeadlineDateTime)) {
            // 申报加收费率
            BigDecimal venueSurchargeRate = venueRule.getSurchargeRate();
            if (venueSurchargeRate.compareTo(BigDecimal.ZERO) != 0) {
                venueSurchargeRate = venueSurchargeRate.movePointLeft(2);
            }
            BigDecimal overtimeSurcharge = WorkOvertimeService.calculateOvertimeSurcharge(overtimeAmount, venueSurchargeRate);
            workOvertimeDTO.setOvertimeSurcharge(overtimeSurcharge);
            shouldPayVenueAmount = overtimeAmount.add(overtimeSurcharge);
        }
        workOvertimeDTO.setOvertimeAmount(overtimeAmount);
        workOvertimeDTO.setShouldPayAmount(shouldPayVenueAmount);
        workOvertimeDTO.setPeriodOnePrice(venuePeriodOnePrice);
        workOvertimeDTO.setPeriodTwoPrice(venuePeriodTwoPrice);
        workOvertimeDTO.setTimeUnit(timeUnit);
    }

    /**
     * 按照场馆规则计算时段1加班时间段，时段2加班时间段，时段1加班计时，时段2加班计时
     *
     * @param workOvertimeDTO the work overtime dto
     * @param venueRule       the work overtime rule vo
     */
    private static void calculateVenuePeriod(WorkOvertimeDTO workOvertimeDTO, WorkOvertimeRuleVO venueRule) {
        LocalTime submitPeriodOneBegin = venueRule.getSubmitPeriodOneBegin();
        LocalTime submitPeriodOneEnd = venueRule.getSubmitPeriodOneEnd();
        LocalTime submitPeriodTwoBegin = venueRule.getSubmitPeriodTwoBegin();
        LocalTime submitPeriodTwoEnd = venueRule.getSubmitPeriodTwoEnd();

        LocalTime overtimeStart = workOvertimeDTO.getOvertimeStart();
        LocalTime overtimeEnd = workOvertimeDTO.getOvertimeEnd();

        LocalDate overtimeDate = workOvertimeDTO.getOvertimeDate();
        Map<String, LocalTime> periodOneMap = WorkOvertimeService.calculatePeriodBeginAndEnd(submitPeriodOneBegin, submitPeriodOneEnd, overtimeStart, overtimeEnd, overtimeDate);
        Map<String, LocalTime> periodTwoMap = WorkOvertimeService.calculatePeriodBeginAndEnd(submitPeriodTwoBegin, submitPeriodTwoEnd, overtimeStart, overtimeEnd, overtimeDate);
        // 时段1的开始时间结束时间
        LocalTime periodOneBegin = periodOneMap.get("begin");
        LocalTime periodOneEnd = periodOneMap.get("end");
        // 计算时段1的时间时长
        double periodOne = WorkOvertimeService.calculatePeriod(periodOneBegin, periodOneEnd, overtimeDate);
        // 时段2的开始时间结束时间
        LocalTime periodTwoBegin = periodTwoMap.get("begin");
        LocalTime periodTwoEnd = periodTwoMap.get("end");
        // 计算时段2的时间时长
        double periodTwo = WorkOvertimeService.calculatePeriod(periodTwoBegin, periodTwoEnd, overtimeDate);
        // 总加班时长
        double reportOvertimePeriod = periodOne + periodTwo;

        workOvertimeDTO.setPeriodOneBegin(periodOneBegin);
        workOvertimeDTO.setPeriodOneEnd(periodOneEnd);
        workOvertimeDTO.setPeriodOne(periodOne);
        workOvertimeDTO.setPeriodTwoBegin(periodTwoBegin);
        workOvertimeDTO.setPeriodTwoEnd(periodTwoEnd);
        workOvertimeDTO.setPeriodTwo(periodTwo);
        workOvertimeDTO.setReportOvertimePeriod(reportOvertimePeriod);
    }


    /**
     * 主场加班申请转为场馆加班申请.
     *
     * @param homeWorkOvertime the home work overtime
     * @return the work overtime dto
     */
    private static WorkOvertimeDTO homeWorkOvertimeToVenue(WorkOvertime homeWorkOvertime) {
        CopyOptions copyOptions = CopyOptions.create()
                .setIgnoreProperties(CommonConstants.COMMON_IGNORE_PROPERTIES);
        WorkOvertimeDTO venueWorkOvertimeDTO = BeanUtil.toBean(homeWorkOvertime, WorkOvertimeDTO.class, copyOptions);
        venueWorkOvertimeDTO.setWorkOvertimeNumber(null);
        venueWorkOvertimeDTO.setSpWorkOvertimeNumber(homeWorkOvertime.getWorkOvertimeNumber());
        venueWorkOvertimeDTO.setOvertimeArea(null);
        venueWorkOvertimeDTO.setTimeUnit(null);
        venueWorkOvertimeDTO.setPeriodOneBegin(null);
        venueWorkOvertimeDTO.setPeriodOneEnd(null);
        venueWorkOvertimeDTO.setPeriodTwoBegin(null);
        venueWorkOvertimeDTO.setPeriodTwoEnd(null);
        venueWorkOvertimeDTO.setReportOvertimePeriod(null);
        venueWorkOvertimeDTO.setPeriodOne(null);
        venueWorkOvertimeDTO.setPeriodTwo(null);
        venueWorkOvertimeDTO.setActualDepartTime(null);
        venueWorkOvertimeDTO.setActualOvertimePeriod(null);
        venueWorkOvertimeDTO.setPeriodOnePrice(null);
        venueWorkOvertimeDTO.setPeriodTwoPrice(null);
        venueWorkOvertimeDTO.setOvertimeAmount(null);
        venueWorkOvertimeDTO.setOvertimeExtraAmount(null);
        venueWorkOvertimeDTO.setOvertimeSurcharge(null);
        venueWorkOvertimeDTO.setShouldPayAmount(null);
        venueWorkOvertimeDTO.setActualPayAmount(null);
        venueWorkOvertimeDTO.setProfitRate(null);
        venueWorkOvertimeDTO.setPayWay(null);
        venueWorkOvertimeDTO.setPayTime(null);
        venueWorkOvertimeDTO.setPayStatus(null);
        venueWorkOvertimeDTO.setWorkOrderStatus(null);
        venueWorkOvertimeDTO.setBelongSystem(MenuAffiliationEnum.P_COMPOSITE_VENUE.getCode());
        return venueWorkOvertimeDTO;
    }

}
